/*
 * To change this template, choose Tools | Templates and open the template in
 * the editor.
 */
package terrain;

import shape3d.ShapeUtilities;
import shape3d.MeshData;
import com.jme3.scene.Geometry;
import com.jme3.renderer.Camera;
import com.jme3.math.Vector3f;
import com.jme3.math.FastMath;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;


/**
 * <code>TerrainSurface</code> defines a flat surface in a terrain. Its borders
 * are defined by wether or not a given terrainHeight is over or under the terrain.
 * A TerrainSurfaceFactory is required to create surfaceLines which again are
 * required to create the actual terrain surface mesh.
 * @author Vemund Kvam
 */
public class TerrainSurface extends Geometry{
    protected Map<String, TerrainSurfaceListener> listeners = new HashMap<String, TerrainSurfaceListener>();
    protected ArrayList surfaceLines = new ArrayList<Geometry>();
    protected Camera camera;
    
    TerrainSurfaceInfo terrainSurfaceInfo;
    //private Geometry terrainSurfaceGeometry;
    com.vividsolutions.jts.geom.Geometry jtsTerrainSurfaceGeometry;
    com.vividsolutions.jts.geom.Geometry jtsTerrainBoundingBox;
    float heightMapHeight;
    float volume;
    // The base TerrainSurface class for when heightMapHeight will be supplied later.
    public TerrainSurface(String name,TerrainSurfaceInfo terrainSurfaceInfo){
        super(name);        
        if (terrainSurfaceInfo == null) {
            throw new NullPointerException();
        }
    this.terrainSurfaceInfo = terrainSurfaceInfo;
    this.volume = 0;
    } 
    public TerrainSurface(String name,TerrainSurfaceInfo terrainSurfaceInfo,float heightMapHeight){
        super(name);        
        if (terrainSurfaceInfo == null) {
        throw new NullPointerException();
        }
    this.terrainSurfaceInfo = terrainSurfaceInfo;
    this.heightMapHeight = heightMapHeight;
    this.volume = 0;
    }
    
    public void setTerrainHeight(float terrainHeight){
    this.heightMapHeight = terrainHeight;
    }
    public ArrayList<com.vividsolutions.jts.geom.Geometry> getSurfaceLines(){    
    return surfaceLines;
    }
    public void addLine(com.vividsolutions.jts.geom.Geometry line){
    surfaceLines.add(line);
    }
    public void addVolume(float addVolume){
    volume+=addVolume;
    }
    public void resetVolumeCount(){
    volume = 0;
    }    
    public float getVolume(){
    return volume*terrainSurfaceInfo.terrain.getLocalScale().x*terrainSurfaceInfo.terrain.getLocalScale().z;
    }
    public Geometry createSurfaceGeometry(Vector3f guidePoint){
    jtsTerrainSurfaceGeometry = ShapeUtilities.createJtsGeometry(getSurfaceLines(),terrainSurfaceInfo.lod+FastMath.ZERO_TOLERANCE*10);
    jtsTerrainBoundingBox = jtsTerrainSurfaceGeometry.getEnvelope();

    this.mesh = ShapeUtilities.createMeshFromJtsGeometry(jtsTerrainSurfaceGeometry).createMesh();
    this.scale(terrainSurfaceInfo.terrain.getLocalScale().x,1,terrainSurfaceInfo.terrain.getLocalScale().z);
    
    Vector3f terrainSurfaceLoc = this.getLocalTranslation();
    terrainSurfaceLoc.y = heightMapHeight*terrainSurfaceInfo.terrain.getLocalScale().y;
    this.setLocalTranslation(terrainSurfaceLoc);

    return this;
    }
    public void updateMesh(){
    jtsTerrainSurfaceGeometry = ShapeUtilities.createJtsGeometry(getSurfaceLines(),terrainSurfaceInfo.lod+FastMath.ZERO_TOLERANCE*10);
    jtsTerrainBoundingBox = jtsTerrainSurfaceGeometry.getEnvelope();    
    MeshData terrainMesh = ShapeUtilities.createMeshFromJtsGeometry(jtsTerrainSurfaceGeometry);
    setMesh(terrainMesh.createMesh());
    setLocalTranslation(0,heightMapHeight*terrainSurfaceInfo.terrain.getLocalScale().y,0);    
    }
    public boolean isInsideBoundingBox(Vector3f location){
        if(jtsTerrainBoundingBox.contains(ShapeUtilities.toPoint(location.divide(terrainSurfaceInfo.getTerrain().getLocalScale())))){
        return true;
        }
    return false;
    }
    public boolean isInsidePolygon(Vector3f location){
        if(jtsTerrainSurfaceGeometry.contains(ShapeUtilities.toPoint(location.divide(terrainSurfaceInfo.getTerrain().getLocalScale())))){
        return true;
        }
    return false;
    }    
    
    
    @Override
    public void updateLogicalState(float tpf){
    super.updateLogicalState(tpf);
        if(camera!=null)
        {
        Vector3f camLoc = camera.getLocation();
            for (TerrainSurfaceListener l : this.listeners.values()) {
                if(isInsideBoundingBox(camLoc)){
                    if(isInsidePolygon(camLoc)){
                        float length = camLoc.y-heightMapHeight*terrainSurfaceInfo.getTerrain().getLocalScale().y;
                        if(length>0){
                        l.insideAbove(this,length);
                        }
                        else{
                        l.insideBelow(this,length);
                        }
                    }
                }
            }    
        }
    }
   public void setCamera(Camera cam){
   this.camera = cam;
   }
   public void addListener(String id, TerrainSurfaceListener listener) {
        this.listeners.put(id, listener);
    }
    public void removeListener(String id) {
        this.listeners.remove(id);
    }    
}
